package com.xnyzc.lhy.nio.netty;

import com.alibaba.fastjson.JSONObject;
import com.xnyzc.lhy.common.cache.NettyChannelMap;
import com.xnyzc.lhy.common.component.config.security.JwtTokenUtil;
import com.xnyzc.lhy.common.component.config.security.TokenUser;
import com.xnyzc.lhy.common.component.redis.RedisCommon;
import com.xnyzc.lhy.common.constant.MethodConstant;
import com.xnyzc.lhy.common.exception.PangException;
import com.xnyzc.lhy.common.penum.EErrorCode;
import com.xnyzc.lhy.common.util.BasicDataUtil;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.nio.entity.netty.NettyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Objects;

/**
 * Netty业务逻辑层
 *
 * @author liuju
 * @create 2019-07-17 13:23
 */
@Slf4j
@Component
public class NettyServerService {
    public static NettyServerService nettyServerService;

    public static Hashtable<String, String> table;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private NettyCommon nettyCommon;

    @Autowired
    private RedisCommon redisCommon;

    static {
        if (table == null) {
            table = new Hashtable<>();
        }
    }


    @PostConstruct
    public void init() {
        nettyServerService = this;
        nettyServerService.nettyCommon = this.nettyCommon;
    }

    private NettyMessage convert(ChannelHandlerContext ctx, Object msg) {
        try {
            // ByteBuf转String
            ByteBuf byteBuf = (ByteBuf) msg;
            byte[] req = new byte[byteBuf.readableBytes()];
            byteBuf.readBytes(req);
            msg = new String(req, "UTF-8");

            // log.info("客户端数据：{}", msg);

            // 验证即时通讯命令是否正确有效
            if (BasicDataUtil.isEmpty(msg)) {
                throw PangException.create(EErrorCode.noData, "消息为空");
            }

            NettyMessage nettyMessage = JSONObject.parseObject((String) msg, NettyMessage.class);

            if (CheckUtil.objIsEmpty(nettyMessage)) {
                throw PangException.create(EErrorCode.dataParseError, "消息解析失败");
            }
            return nettyMessage;
        } catch (Exception e) {
            log.error("netty 异常 {}:{}:{}", ctx, msg.toString(), e.getMessage());
        }
        return null;
    }

    public boolean regist(ChannelHandlerContext ctx, Object msg) {
        NettyMessage nettyMessage = convert(ctx, msg);
        if (nettyMessage != null) {
            Integer code = nettyMessage.getCode();
            String method = nettyMessage.getMethod();
            if (MethodConstant.DRIVER_REGIST.equals(method)) {
                JSONObject jsonCon = JSONObject.parseObject(nettyMessage.getCon());
                String token = jsonCon.getString("token");

                TokenUser tokenUser = JwtTokenUtil.getUserFromToken(token);
                if (jwtTokenUtil.validateToken(token, tokenUser)) {
                    log.info("令牌无效");
                    JSONObject json = new JSONObject();
                    json.put("res", "0");
                    NettyServerService.sendMsgToClient(ctx, NettyMsg.setJsonMsg(MethodConstant.DRIVER_REGIST, json.toString()));
                    return false;
                } else {
                    log.info("认证成功");
                    String cityId = jsonCon.getString("cityId");
                    String driverId = jsonCon.getString("driverId");
                    String key = cityId + ":" + driverId;
                    NettyChannelMap.saveData(key, ctx);

                    JSONObject json = new JSONObject();
                    json.put("res", "1");
                    NettyServerService.sendMsgToClient(ctx, NettyMsg.setJsonMsg(MethodConstant.DRIVER_REGIST, json.toString()));
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断客户端要执行什么操作
     *
     * @param ctx
     * @param msg
     */
    public void judgeOperation(ChannelHandlerContext ctx, Object msg) {
        NettyMessage nettyMessage = convert(ctx, msg);
        if (nettyMessage != null) {
            // 获取socket信息，保存相应的socket
            Integer code = nettyMessage.getCode();
            String method = nettyMessage.getMethod();
            if (!Objects.equals(code, 0)) {
                throw PangException.create(EErrorCode.authErr, "非正确消息");
            }

            JSONObject jsonCon = JSONObject.parseObject(nettyMessage.getCon());

            // 进行业务处理
            methodService(ctx, method, jsonCon);
        }
    }

    /**
     * 处理业务方法
     *
     * @param ctx
     * @param method
     * @param jsonCon
     */
    private void methodService(ChannelHandlerContext ctx, String method, JSONObject jsonCon) {
        switch (method) {
            // 司机上传位置
            case MethodConstant.GDLOC:
                nettyCommon.genDriverUploadLocHandle(ctx, jsonCon, method);
                break;
            //司机端 -- 里程计费[推送 -- 接受]
            case MethodConstant.MILEAGE_BILLING:
                nettyCommon.mileageBilling(ctx, method, jsonCon);
                break;
            //司机端 -- 司机获取订单信息（修改为在司机上报位置时获取订单数据）
//            case MethodConstant.DRIVER_GET_ORDER_MESSAGE:
//                nettyCommon.driverGetOrderMessage(ctx, method, jsonCon);
//                break;
            default:
                break;
        }
    }

    /**
     * 向客户端发送消息
     *
     * @param ctx
     * @param msg
     */
    public static void sendMsgToClient(ChannelHandlerContext ctx, String msg) {
        if (ctx != null) {
            sendMsg(ctx, msg);
        }
    }

    /**
     * 发送消息
     *
     * @param ctx 通道
     * @param msg 信息
     */
    private static void sendMsg(ChannelHandlerContext ctx, String msg) {
        ByteBuf buffer = null;
        try {
            buffer = Unpooled.copiedBuffer((msg).getBytes("GBK"));
        } catch (UnsupportedEncodingException e) {
            log.error("netty sendMsgToClient方法,msg:{}", msg);
        }
        ChannelFuture sync;
        try {
            sync = ctx.writeAndFlush(buffer).sync();
            log.info("推送状态1:" + sync.isSuccess() + "，消息內容:" + msg);
            if (!sync.isSuccess()) {
                // 循环次数
                Integer cycleInt = 10;
                for (int i = 0; i < cycleInt; i++) {
                    ctx.wait(3000);
                    sync = ctx.writeAndFlush(buffer).sync();
                    if (sync.isSuccess()) {
                        break;
                    }
                    log.info("推送不成功，将继续推送:" + msg);
                }
            }
        } catch (Exception e) {
            log.info("推送发生异常，记录：" + msg);
        }
    }

    private static boolean sendMsgGetResult(ChannelHandlerContext ctx, String msg) {
        ByteBuf buffer = null;
        try {
            buffer = Unpooled.copiedBuffer((msg).getBytes("GBK"));
        } catch (UnsupportedEncodingException e) {
            log.error("netty sendMsgToClient方法,msg:{}", msg);
        }
        ChannelFuture sync;
        try {
            sync = ctx.writeAndFlush(buffer).sync();
            log.info("推送状态2:" + sync.isSuccess() + "，消息內容:" + msg);
            if (!sync.isSuccess()) {
                return false;
            }
        } catch (Exception e) {
            log.info("推送发生异常，记录：" + msg);
        }
        return true;
    }

    /**
     * 链接断开 将推送消息记录
     */
    public static void sendMsgToClient(String cacheType, Integer id, String msg) {
        ChannelHandlerContext ctx = NettyChannelMap.getData(cacheType + id);
//        if (ctx != null && ctx.channel().isActive()) {
        ByteBuf buffer = null;
        try {
            buffer = Unpooled.copiedBuffer((msg).getBytes("GBK"));
        } catch (UnsupportedEncodingException e) {
            log.error("netty sendMsgToClient方法,msg:{}", msg);
        }
        ChannelFuture sync;
        try {
            sync = ctx.writeAndFlush(buffer).sync();
            log.info("推送状态3" + sync.isSuccess());
            if (!sync.isSuccess()) {
                // 循环推送次数
                Integer cycle = 10;
                for (int i = 0; i < cycle; i++) {
                    sync = ctx.writeAndFlush(buffer).sync();
                    if (!sync.isSuccess()) {
                        sync = ctx.writeAndFlush(buffer).sync();
                        log.info("推送不成功，将继续推送:" + msg);
                        if (i == 9) {
                            table.put(cacheType + id, msg);
                            ctx.close();
                            log.info("推送发生异常，记录：" + msg);
                        }
                    } else {
                        break;
                    }
                }
            }
        } catch (Exception e) {
            table.put(cacheType + id, msg);
            log.error("推送发生异常，记录：" + msg);
        }
//        } else {
//            table.put(cacheType + id, msg);
//            log.info("链接断开，记录：id=" + cacheType + id + ",消息:" + msg);
//        }
    }

    /**
     * 记录推送不成功消息，并在心跳连接续推
     *
     * @param token
     */
    public static void resendMsg(String token) {
        String msg = table.get(token);
        ChannelHandlerContext ctx = NettyChannelMap.getData(token);
//        if (BasicDataUtil.isNotEmpty(msg) && ctx != null && ctx.channel().isActive()) {
        ByteBuf buffer = null;
        try {
            buffer = Unpooled.copiedBuffer((msg).getBytes("GBK"));
        } catch (UnsupportedEncodingException e) {
            log.error("netty resendMsg方法,msg:{}", msg);
        }
        ChannelFuture sync;
        try {
            sync = ctx.writeAndFlush(buffer).sync();
            log.info("重发异常推送状态" + sync.isSuccess() + "，位置：" + token + "，消息内容：" + msg);
            if (sync.isSuccess()) {
                table.remove(token);
            }
        } catch (Exception e) {
            log.info("重发推送发生异常：" + msg);
        }
//        }
    }

}
